#pragma once

#include "sfc/collections.h"

namespace sfc::serde {

template <class T>
struct Serde {
  const T& _inn;

 public:
  auto serialize(const auto& se) {
    auto info = reflect_struct(_inn);
    return se.ser_struct(info);
  }
};

template <>
struct Serde<bool> {
  bool _inn;

 public:
  auto serialize(const auto& se) const {
    return se.ser_bool(_inn);
  }

  static auto deserialize(const auto& de) -> Option<bool> {
    return de.des_bool();
  }
};

template <trait::Int T>
struct Serde<T> {
  T _inn;

 public:
  auto serialize(const auto& se) const {
    return se.ser_int(static_cast<i64>(_inn));
  }

  static auto deserialize(const auto& de) -> Option<T> {
    return de.des_int().map([&](auto x) { return static_cast<T>(x); });
  }
};

template <trait::Float T>
struct Serde<T> {
  T _inn;

 public:
  auto serialize(const auto& se) const {
    return se.ser_flt(_inn);
  }

  static auto deserialize(const auto& de) -> Option<T> {
    return de.des_int().map([&](auto x) { return static_cast<T>(x); });
  }
};

template <trait::Enum T>
struct Serde<T> {
  const T& _inn;

 public:
  auto serialize(const auto& se) const {
    const auto s = reflect::enum_name(_inn);
    return se.ser_str(s);
  }

  static auto deserialize(const auto& de) -> Option<T> {
    return de.des_str().and_then([](auto& x) { return reflect::enum_from_name<T>(x); });
  }
};

template <>
struct Serde<String> {
  const String& _inn;

 public:
  auto serialize(const auto& se) const {
    return se.ser_str();
  }

  static auto deserialize(const auto& de) -> Option<String> {
    return de.des_str().map([&](auto x) { return String{x}; });
  }
};

template <class T, usize N>
struct Serde<T[N]> {
  const T (&_inn)[N];

 public:
  auto serialize(const auto& se) const {
    if constexpr (requires { Str{_inn}; }) {
      return se.ser_str(_inn);
    } else {
      return se.ser_list(Slice{_inn}.iter());
    }
  }

  static auto deserialize(const auto& de, T (&res)[N]) -> bool {
    return de.des_list([&, idx = 0](auto& ele) mutable { return ele.des(res[idx++]); });
  };
};

template <class T>
struct Serde<Vec<T>> {
  const Vec<T>& _inn;

 public:
  auto serialize(const auto& se) const {
    return se.ser_list(_inn.iter());
  }

  static auto deserialize(const auto& de) -> Option<Vec<T>>;
};

template <class K, class V>
struct Serde<VecMap<K, V>> {
  const VecMap<K, V>& _inn;

 public:
  auto serialize(const auto& se) const {
    return se.ser_dict(_inn.iter());
  }

  static auto deserialize(const auto& de) -> Option<VecMap<K, V>>;
};

template <class T>
Serde(const T&) -> Serde<T>;

}  // namespace sfc::serde
